/**
 * iVprog/Alice: http://www.matematica.br/ivprog, http://www.alice.org
 * @author Jason Pratt / LOB; RR
 * 
 * @description Right panel with tabs to working areas: main function and others functions
 *              Define panels and actions, like, right click to "close" the tab
 * @see edu.cmu.cs.stage3.alice.authoringtool.JAliceFrame that extends JFrame, will contain 'DragFromComponent'
 */

package edu.cmu.cs.stage3.alice.authoringtool;

import edu.cmu.cs.stage3.resourceBundle.I18n;
import edu.cmu.cs.stage3.alice.authoringtool.py.AliceStyle;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.datatransfer.DataFlavor;

import javax.swing.JTabbedPane;

import edu.cmu.cs.stage3.alice.authoringtool.datatransfer.CallToUserDefinedQuestionPrototypeReferenceTransferable;
import edu.cmu.cs.stage3.alice.authoringtool.datatransfer.CallToUserDefinedResponsePrototypeReferenceTransferable;
import edu.cmu.cs.stage3.alice.authoringtool.datatransfer.ElementReferenceTransferable;
import edu.cmu.cs.stage3.alice.core.Element;
import edu.cmu.cs.stage3.util.StringObjectPair;

public class TabbedEditorComponent extends javax.swing.JPanel {

 protected AuthoringTool authoringTool;
 protected EditorManager editorManager;
 protected EditorDropTargetListener editorDropTargetListener = new EditorDropTargetListener();
 protected java.util.HashMap componentsToEditors = new java.util.HashMap();
 protected RightClickListener rightClickListener = new RightClickListener();
 protected NameListener nameListener = new NameListener();
 protected DeletionListener deletionListener = new DeletionListener();
 protected Runnable closeAllTabsRunnable = new Runnable() {
  public void run() {
   TabbedEditorComponent.this.closeAllTabs();
  }
 };

 public TabbedEditorComponent (AuthoringTool authoringTool) {
  this.authoringTool = authoringTool;
  this.editorManager = authoringTool.getEditorManager();
  jbInit();
  guiInit();

  this.setForeground(AliceStyle.fgColor); // foreground color of the frame
  this.setBackground(AliceStyle.bgColor); // backgroud color of the frame
  //  miscInit();
  }

 //////////////////
 // Autogenerated
 //////////////////

 JTabbedPane tabbedPane = new JTabbedPane();
 BorderLayout borderLayout1 = new BorderLayout();

 private void jbInit () {
  this.setLayout(borderLayout1);
  this.setOpaque(false);
  // tabbedPane.setBackground(java.awt.Color.white); // AliceStyle.bgColor); // backgroud color of the
  this.add(tabbedPane, BorderLayout.CENTER);
  }
 //////////////////

 private void guiInit () {
  tabbedPane.setUI( new edu.cmu.cs.stage3.alice.authoringtool.util.AliceTabbedPaneUI() );
  setDropTarget( new java.awt.dnd.DropTarget( this, editorDropTargetListener ) );
  tabbedPane.setDropTarget( new java.awt.dnd.DropTarget( tabbedPane, editorDropTargetListener ) );
  tabbedPane.addMouseListener( rightClickListener );
  int fontSize = 12;
  tabbedPane.setFont( new java.awt.Font( "SansSerif", java.awt.Font.PLAIN, (int)fontSize) );
  }

 public void setWorld( edu.cmu.cs.stage3.alice.core.World world) {
  stopListeningToTree( world );
  closeAllTabs();
  if (world != null) {
   startListeningToTree( world );
   }
 }

 public void editObject (Object object, Class editorClass, boolean switchToNewTab) {
  if ((object == null) || (editorClass == null)) { //TODO: this is a hack
   closeAllTabs();
  } else { // if ((object == null) || (editorClass == null))
   if (! isObjectBeingEdited( object )) { //TODO: allow editing of the same object with different editors
    Editor editor = editorManager.getEditorInstance( editorClass );
    if (editor != null) {
     componentsToEditors.put( editor.getJComponent(), editor );

     // Chama: edu.cmu.cs.stage3.alice.authoringtool.util.EditorUtilities.editObject(EditorUtilities.java:96)
     // Chamado: edu.cmu.cs.stage3.alice.authoringtool.AuthoringTool.editObject(AuthoringTool.java:737)
     // At the end does 'new edu.cmu.cs.stage3.alice.authoringtool.editors.compositeeditor.InsertPanel()'
     edu.cmu.cs.stage3.alice.authoringtool.util.EditorUtilities.editObject( editor, object );

     String repr = AuthoringToolResources.getReprForValue( object, true );
     Object iconObject = object;
     if (object instanceof edu.cmu.cs.stage3.alice.core.question.userdefined.UserDefinedQuestion) {
      edu.cmu.cs.stage3.alice.core.question.userdefined.UserDefinedQuestion udq = (edu.cmu.cs.stage3.alice.core.question.userdefined.UserDefinedQuestion)object;
      if (edu.cmu.cs.stage3.alice.core.List.class.isAssignableFrom( udq.getValueClass() )) {
       edu.cmu.cs.stage3.alice.core.List list = (edu.cmu.cs.stage3.alice.core.List)udq.getValue();
       iconObject = I18n.getString("tabTypeList") + list.valueClass.getClassValue().getName(); // "types/lists/"
      } else {
       iconObject = I18n.getString("tabType") + udq.getValueClass().getName(); // "types/"
       }
      }
     javax.swing.ImageIcon icon = AuthoringToolResources.getIconForValue( iconObject );
     tabbedPane.addTab( repr, icon, editor.getJComponent() );
     if (switchToNewTab) {
      tabbedPane.setSelectedComponent( editor.getJComponent() );
      tabbedPane.getSelectedComponent().setVisible( true ); //HACK for Java Bug 4190719
      }
     if (object instanceof edu.cmu.cs.stage3.alice.core.Element) {
      ((edu.cmu.cs.stage3.alice.core.Element)object).name.addPropertyListener( nameListener );
      }
     authoringTool.saveTabs();
    } else { //  "failed to create editor for " 
     AuthoringTool.getInstance().showErrorDialog(I18n.getString("tabFailedEditor") + object + ", " + editorClass, null );
     }
    } // if (! isObjectBeingEdited( object ))
   else if (switchToNewTab) {
    for( int i = 0; i < tabbedPane.getTabCount(); i++) {
     java.awt.Component component = tabbedPane.getComponentAt( i );
     if (component != null) {
      Editor editor = (Editor)componentsToEditors.get( component );
      if (editor != null) {
       if (editor.getObject() == object) {
        tabbedPane.setSelectedIndex( i );
        tabbedPane.getSelectedComponent().setVisible( true ); //HACK for Java Bug 4190719
        break;
        }
       }
      }
     }
    } // else if (switchToNewTab)
   } // else if ((object == null) || (editorClass == null))
  } // public void editObject(Object object, Class editorClass, boolean switchToNewTab)

 public Object getObjectBeingEdited () {
  java.awt.Component component = tabbedPane.getSelectedComponent();
  if (component != null) {
   Editor editor = (Editor)componentsToEditors.get( component );
   if (editor != null) {
    return editor.getObject();
    }
   }
  return null;
  }

 public Object getObjectBeingEditedAt (int index) {
  java.awt.Component component = tabbedPane.getComponentAt( index );
  if (component != null) {
   Editor editor = (Editor)componentsToEditors.get( component );
   if (editor != null) {
    return editor.getObject();
    }
   }
  return null;
  }

 public Object[] getObjectsBeingEdited () {
  java.awt.Component[] components = tabbedPane.getComponents();
  if (components != null) {
   java.util.ArrayList objects = new java.util.ArrayList();
   //LOB for (Component component2 : components)
   int t_tam = components!=null ? components.length : 0;
   for (int i_i=0; i_i<t_tam; i_i++) {
    Editor editor = (Editor)componentsToEditors.get( components[i_i] ); //L ( component2 )
    if (editor != null) {
     objects.add( editor.getObject() );
     }
    }
   return objects.toArray();
   }
  return null;
  }
 
 public Editor getCurrentEditor () {
  java.awt.Component component = tabbedPane.getSelectedComponent();
  if (component != null) {
   return (Editor)componentsToEditors.get( component );
   }
  return null;
  }

 public Editor[] getEditors () {
  java.awt.Component[] components = tabbedPane.getComponents();
  if (components != null) {
   java.util.ArrayList editors = new java.util.ArrayList();
   //LOB for (Component component2 : components)
   int t_tam = components!=null ? components.length : 0;
   for (int i_i=0; i_i<t_tam; i_i++) {
    Editor editor = (Editor)componentsToEditors.get( components[i_i] ); //L ( component2 )
    if (editor != null) {
     editors.add( editor );
     }
    }
   return (Editor[])editors.toArray( new Editor[0] );
   }
  return null;
  }

 public void closeTab (int index) {
  java.awt.Component component = tabbedPane.getComponentAt( index );
  if (component != null) {
   Editor editor = (Editor)componentsToEditors.get( component );
   if (editor != null) {
    Object object = editor.getObject();
    tabbedPane.removeTabAt( index );
    edu.cmu.cs.stage3.alice.authoringtool.util.EditorUtilities.editObject( editor, null );
    editorManager.releaseEditorInstance( editor );
    componentsToEditors.remove( component );
    if ((object instanceof edu.cmu.cs.stage3.alice.core.Element) && (! isObjectBeingEdited( object ))) {
     ((edu.cmu.cs.stage3.alice.core.Element)object).name.removePropertyListener( nameListener );
     }
    authoringTool.saveTabs();
    }
  } else { // "no editor to close at "
   AuthoringTool.getInstance().showErrorDialog(I18n.getString("tabNoEditor") + index, null );
   }
  }

 public void closeAllTabs () {
  while (tabbedPane.getTabCount() > 0) {
   closeTab( 0 );
   }
  }

 public boolean isObjectBeingEdited (Object o) {
  for (int i=0; i<tabbedPane.getTabCount(); i++) {
   java.awt.Component component = tabbedPane.getComponentAt( i );
   if (component != null) {
    Editor editor = (Editor)componentsToEditors.get( component );
    if (editor != null) {
     if (editor.getObject() == o) {
      return true;
      }
     }
    }
   }
  return false;
  }

 // Action to 'right click' over a tab function
 protected class RightClickListener extends edu.cmu.cs.stage3.alice.authoringtool.util.CustomMouseAdapter {
  //Rem @Override
  public void popupResponse( final java.awt.event.MouseEvent ev) {
   final int index = TabbedEditorComponent.this.tabbedPane.getUI().tabForCoordinate( TabbedEditorComponent.this.tabbedPane, ev.getX(), ev.getY() );
   if ((index >= 0) && (index < TabbedEditorComponent.this.tabbedPane.getTabCount())) {
    Runnable closeTabRunnable = new Runnable() {
     public void run() {
      TabbedEditorComponent.this.closeTab( index );
      }
     };
    java.util.Vector structure = new java.util.Vector();
    structure.add( new StringObjectPair(I18n.getString("msgTabClose") + TabbedEditorComponent.this.tabbedPane.getTitleAt(index), closeTabRunnable) ); // "Close "
    structure.add( new StringObjectPair(I18n.getString("msgTabCloseAll"), closeAllTabsRunnable) ); // "Close All"
    edu.cmu.cs.stage3.alice.authoringtool.util.PopupMenuUtilities.createAndShowPopupMenu( structure, TabbedEditorComponent.this.tabbedPane, ev.getX(), ev.getY() );
    }
   }
  }

 protected class EditorDropTargetListener implements java.awt.dnd.DropTargetListener {
  protected void checkDrag( java.awt.dnd.DropTargetDragEvent dtde) {
   //TODO: better feedback
   if (AuthoringToolResources.safeIsDataFlavorSupported( dtde, CallToUserDefinedResponsePrototypeReferenceTransferable.callToUserDefinedResponsePrototypeReferenceFlavor )) {
    dtde.acceptDrag( java.awt.dnd.DnDConstants.ACTION_MOVE );
    return;
   } else if (AuthoringToolResources.safeIsDataFlavorSupported(dtde, CallToUserDefinedQuestionPrototypeReferenceTransferable.callToUserDefinedQuestionPrototypeReferenceFlavor )) {
    dtde.acceptDrag( java.awt.dnd.DnDConstants.ACTION_MOVE );
    return;
   } else if (AuthoringToolResources.safeIsDataFlavorSupported(dtde, AuthoringToolResources.getReferenceFlavorForClass( edu.cmu.cs.stage3.alice.core.response.CallToUserDefinedResponse.class ) )) {
    dtde.acceptDrag( java.awt.dnd.DnDConstants.ACTION_MOVE );
    return;
   } else if (AuthoringToolResources.safeIsDataFlavorSupported(dtde, AuthoringToolResources.getReferenceFlavorForClass( edu.cmu.cs.stage3.alice.core.question.userdefined.CallToUserDefinedQuestion.class ) )) {
    dtde.acceptDrag( java.awt.dnd.DnDConstants.ACTION_MOVE );
    return;
   } else {
    java.awt.datatransfer.DataFlavor[] flavors = AuthoringToolResources.safeGetCurrentDataFlavors(dtde);
    if (flavors != null) {
     //LOB for (DataFlavor flavor : flavors)
     int t_tam = flavors!=null ? flavors.length : 0;
     for (int i_i=0; i_i<t_tam; i_i++) {
      Class c = flavors[i_i].getRepresentationClass(); //L flavor.getRepresentationClass()
      if (edu.cmu.cs.stage3.alice.authoringtool.util.EditorUtilities.getBestEditor( c ) != null) {
       dtde.acceptDrag( java.awt.dnd.DnDConstants.ACTION_MOVE );
       return;
       }
      }
     } // if (flavors != null)
    }
   dtde.rejectDrag();
   }

  public void dragEnter (java.awt.dnd.DropTargetDragEvent dtde) {
   // System.out.println("\n\nTabbedEditorComponent.java: dropEnter(java.awt.dnd.DropTargetDropEvent)");
   checkDrag( dtde );
   }

  public void dragOver (java.awt.dnd.DropTargetDragEvent dtde) {
   checkDrag( dtde );
   }

  public void dropActionChanged (java.awt.dnd.DropTargetDragEvent dtde) {
   checkDrag( dtde );
   }

  public void dragExit (java.awt.dnd.DropTargetEvent dte) {
   //TODO: feedback
   }

  public void drop (java.awt.dnd.DropTargetDropEvent dtde) {
   try {
    // System.out.println("\n\nTabbedEditorComponent.java: drop(java.awt.dnd.DropTargetDropEvent)");
    Object o = null;
    if (AuthoringToolResources.safeIsDataFlavorSupported(dtde, AuthoringToolResources.getReferenceFlavorForClass( edu.cmu.cs.stage3.alice.core.response.CallToUserDefinedResponse.class ) )) {
     dtde.acceptDrop( java.awt.dnd.DnDConstants.ACTION_MOVE );
     java.awt.datatransfer.Transferable transferable = dtde.getTransferable();
     o = transferable.getTransferData( AuthoringToolResources.getReferenceFlavorForClass( edu.cmu.cs.stage3.alice.core.response.CallToUserDefinedResponse.class ) );
     o = ((edu.cmu.cs.stage3.alice.core.response.CallToUserDefinedResponse)o).userDefinedResponse.getUserDefinedResponseValue();
    } else if (AuthoringToolResources.safeIsDataFlavorSupported(dtde, AuthoringToolResources.getReferenceFlavorForClass( edu.cmu.cs.stage3.alice.core.question.userdefined.CallToUserDefinedQuestion.class ) )) {
     dtde.acceptDrop( java.awt.dnd.DnDConstants.ACTION_MOVE );
     java.awt.datatransfer.Transferable transferable = dtde.getTransferable();
     o = transferable.getTransferData( AuthoringToolResources.getReferenceFlavorForClass( edu.cmu.cs.stage3.alice.core.question.userdefined.CallToUserDefinedQuestion.class ) );
     o = ((edu.cmu.cs.stage3.alice.core.question.userdefined.CallToUserDefinedQuestion)o).userDefinedQuestion.getUserDefinedQuestionValue();
    } else if (AuthoringToolResources.safeIsDataFlavorSupported(dtde, ElementReferenceTransferable.elementReferenceFlavor )) {
     dtde.acceptDrop( java.awt.dnd.DnDConstants.ACTION_MOVE );
     java.awt.datatransfer.Transferable transferable = dtde.getTransferable();
     o = transferable.getTransferData( ElementReferenceTransferable.elementReferenceFlavor );
    } else if (AuthoringToolResources.safeIsDataFlavorSupported(dtde, CallToUserDefinedResponsePrototypeReferenceTransferable.callToUserDefinedResponsePrototypeReferenceFlavor )) {
     dtde.acceptDrop( java.awt.dnd.DnDConstants.ACTION_MOVE );
     java.awt.datatransfer.Transferable transferable = dtde.getTransferable();
     edu.cmu.cs.stage3.alice.authoringtool.util.CallToUserDefinedResponsePrototype callToUserDefinedResponsePrototype = (edu.cmu.cs.stage3.alice.authoringtool.util.CallToUserDefinedResponsePrototype)transferable.getTransferData( CallToUserDefinedResponsePrototypeReferenceTransferable.callToUserDefinedResponsePrototypeReferenceFlavor );
     o = callToUserDefinedResponsePrototype.getActualResponse();
    } else if (AuthoringToolResources.safeIsDataFlavorSupported(dtde, CallToUserDefinedQuestionPrototypeReferenceTransferable.callToUserDefinedQuestionPrototypeReferenceFlavor )) {
     dtde.acceptDrop( java.awt.dnd.DnDConstants.ACTION_MOVE );
     java.awt.datatransfer.Transferable transferable = dtde.getTransferable();
     edu.cmu.cs.stage3.alice.authoringtool.util.CallToUserDefinedQuestionPrototype callToUserDefinedQuestionPrototype = (edu.cmu.cs.stage3.alice.authoringtool.util.CallToUserDefinedQuestionPrototype)transferable.getTransferData( CallToUserDefinedQuestionPrototypeReferenceTransferable.callToUserDefinedQuestionPrototypeReferenceFlavor );
     o = callToUserDefinedQuestionPrototype.getActualQuestion();
    } else {
     java.awt.datatransfer.DataFlavor[] flavors = AuthoringToolResources.safeGetCurrentDataFlavors(dtde);
     if (flavors != null) {
      //LOB for (DataFlavor flavor : flavors)
      int t_tam = flavors!=null ? flavors.length : 0;
      for (int i_i=0; i_i<t_tam; i_i++) {
       Class c = flavors[i_i].getDefaultRepresentationClass(); //L flavor.getDefaultRepresentationClass()
       if (edu.cmu.cs.stage3.alice.authoringtool.util.EditorUtilities.getBestEditor( c ) != null) {
        dtde.acceptDrop( java.awt.dnd.DnDConstants.ACTION_MOVE );
        java.awt.datatransfer.Transferable transferable = dtde.getTransferable();
        o = transferable.getTransferData( flavors[i_i] ); //L ( flavor )
        break;
        }
       } // for (int i_i=0; i_i<t_tam; i_i++)
      } // if (flavors != null)
     }
    if (o != null) {
     Class editorClass = edu.cmu.cs.stage3.alice.authoringtool.util.EditorUtilities.getBestEditor( o.getClass() );
     if (editorClass != null) {
      TabbedEditorComponent.this.editObject( o, editorClass, true );
      }
     dtde.dropComplete( true );
    } else {
     System.err.println("Error: TabbedEditorComponent.java: rejected drop!");
     dtde.rejectDrop();
     dtde.dropComplete( false );
     }
   } catch( java.awt.datatransfer.UnsupportedFlavorException e) { //  "Drop didn't work: bad flavor"
    System.err.println("Error: TabbedEditorComponent.java: UnsupportedFlavorException: "+e.toString());
    AuthoringTool.getInstance().showErrorDialog(I18n.getString("tabFailedBadFlavor"), e );
    dtde.dropComplete( false );
   } catch( java.io.IOException e) { // "Drop didn't work: IOException"
    System.err.println("Error: TabbedEditorComponent.java: IOException: "+e.toString());
    AuthoringTool.getInstance().showErrorDialog(I18n.getString("tabIOExcept"), e );
    dtde.dropComplete( false );
   } catch( Throwable t) { // "Drop didn't work"
    System.err.println("Error: TabbedEditorComponent.java: Throwable: "+t.toString());
    AuthoringTool.getInstance().showErrorDialog(I18n.getString("tabFailedDrop"), t );
    dtde.dropComplete( false );
    }
   } // void drop (java.awt.dnd.DropTargetDropEvent dtde)

  } // protected class EditorDropTargetListener implements java.awt.dnd.DropTargetListener

 protected class NameListener implements edu.cmu.cs.stage3.alice.core.event.PropertyListener {
  public void propertyChanging( edu.cmu.cs.stage3.alice.core.event.PropertyEvent ev) {}
  public void propertyChanged( edu.cmu.cs.stage3.alice.core.event.PropertyEvent ev) {
   for( int i = 0; i < tabbedPane.getTabCount(); i++) {
    Object object = getObjectBeingEditedAt( i );
    if (object == ev.getProperty().getOwner()) {
     tabbedPane.setTitleAt( i, AuthoringToolResources.getReprForValue( ev.getProperty().getOwner(), true ) );
     }
    }
   }
  }

 protected class DeletionListener implements edu.cmu.cs.stage3.alice.core.event.ChildrenListener {
  public void childrenChanging (edu.cmu.cs.stage3.alice.core.event.ChildrenEvent ev) {}
  public void childrenChanged (edu.cmu.cs.stage3.alice.core.event.ChildrenEvent ev) {
   if (ev.getChangeType() == edu.cmu.cs.stage3.alice.core.event.ChildrenEvent.CHILD_REMOVED) {
    stopListeningToTree( ev.getChild() );
    for( int i = 0; i < tabbedPane.getTabCount(); i++) {
     Object object = getObjectBeingEditedAt( i );
     if (object instanceof edu.cmu.cs.stage3.alice.core.Element) {
      edu.cmu.cs.stage3.alice.core.Element element = (edu.cmu.cs.stage3.alice.core.Element)object;
      if ((element == ev.getChild()) || ev.getChild().isAncestorOf( element )) {
       TabbedEditorComponent.this.closeTab( i );
       i--;
       }
      }
     }
   } else if (ev.getChangeType() == edu.cmu.cs.stage3.alice.core.event.ChildrenEvent.CHILD_INSERTED) {
    startListeningToTree( ev.getChild() );
    }
   }
  }

 protected void startListeningToTree (edu.cmu.cs.stage3.alice.core.Element element) {
  if (element != null) {
   edu.cmu.cs.stage3.alice.core.Element[] descendants = element.getDescendants();
   //LOB for (Element descendant : descendants)
   int t_tam = descendants!=null ? descendants.length : 0;
   for (int i_i=0; i_i<t_tam; i_i++) {
    descendants[i_i].addChildrenListener( deletionListener ); //L descendant.addChildrenListener( deletionListener )
    }
   }
  }

 protected void stopListeningToTree (edu.cmu.cs.stage3.alice.core.Element element) {
  if (element != null) {
   edu.cmu.cs.stage3.alice.core.Element[] descendants = element.getDescendants();
   //LOB for (Element descendant : descendants)
   int t_tam = descendants!=null ? descendants.length : 0;
   for (int i_i=0; i_i<t_tam; i_i++) {
    descendants[i_i].removeChildrenListener( deletionListener ); //L descendant.removeChildrenListener( deletionListener )
    }
   }
  }

 } // public class TabbedEditorComponent extends javax.swing.JPanel
